//////////////////////////////////////////////////////////////////////
//
//  CryEngine Source code
//	
//	File:CSkinInstance.cpp
//  Implementation of CSkinInstance class
//
//	History:
//	September 23, 2004: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////
#include "StdAfx.h"

#include <I3DEngine.h>
#include <Cry_Camera.h>
#include <CryAnimationScriptCommands.h>


#include "ModelMesh.h"
#include "CharacterManager.h"
#include "CryCharAnimationParams.h"
#include "CryCharMorphParams.h"
#include "IRenderAuxGeom.h"
#include "FacialAnimation/FacialInstance.h"
#include "GeomQuery.h"
#include "CharacterInstance.h"


CCharInstance::CCharInstance(const string& strFileName, CCharacterModel* pModel) :
	CSkinInstance(strFileName, pModel, 0xaaaabbbb, 0)
{
	LOADING_TIME_PROFILE_SECTION(g_pISystem);
	Init();
}

void CCharInstance::Init()
{
	MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other,0,"Character Instance");

	CSkinInstance::Init();

	g_nCharInstanceLoaded++;
	m_nInstanceNumber = g_nCharInstanceLoaded;

  m_fUniformScale = 1.0f;

	m_AttachmentManager.m_pSkinInstance=this;
	m_AttachmentManager.m_pSkelInstance=this;
	
	SAFE_RELEASE(m_pFacialInstance);

	if (m_pModel->GetFacialModel())
	{
		m_pFacialInstance = new CFacialInstance(m_pModel->GetFacialModel(),this,this);
		m_pFacialInstance->AddRef();
	}

	m_nForceUpdate=1;
	m_fDeltaTime=0;
	m_fAccDeltaTime=0;
	m_fOriginalDeltaTime=0;

	m_RenderCharLocation.SetIdentity();
	m_PhysEntityLocation.SetIdentity();

	m_SkeletonAnim.InitSkeletonAnim(this,&this->m_SkeletonPose);
	m_SkeletonPose.InitSkeletonPose(this,&this->m_SkeletonAnim);

	// MOVE THIS TO SOME FUNCTIONS WHICH MIGHT BE CALLED FROM INTERFACE (Crysis-k01 compatibility)
  m_IgnoreScalingJoints.resize(0);
  m_IgnoreScalingJoints.push_back("Bip01 Head");
  m_IgnoreScalingJoints.push_back("eye_left_bone");
  m_IgnoreScalingJoints.push_back("eye_right_bone");
  //
  uint32 numJoints		= m_pModel->m_ModelSkeleton.m_arrModelJoints.size();
  m_IgnoreScale.resize(numJoints);
  m_bCustomScale.resize(numJoints);
  m_fCustomScale.resize(numJoints);
  for (uint32 i=0; i<numJoints; i++)
  {
    m_IgnoreScale[i] = false;
    m_bCustomScale[i] = false;
    m_fCustomScale[i] = 1.0f;
    for(int j=0;j<m_IgnoreScalingJoints.size();j++)
    {
      if( strcmp(m_pModel->m_ModelSkeleton.m_arrModelJoints[i].GetJointName(),m_IgnoreScalingJoints[j].c_str())==0 )
      {
        m_IgnoreScale[i] = true;
      }
    }
  }
  //
	m_SkeletonPose.UpdateBBox(1);
	
	m_SkeletonPose.m_bHasPhysics = m_pModel->m_bHasPhysics2;
	m_SkeletonPose.m_bHasPhysicsProxies = m_pModel->m_ModelSkeleton.m_bHasPhysicsProxies;

	m_ResetMode=0;
	m_HideMaster=0;
	m_bWasVisible = 0;
	m_bPlayedPhysAnim = 0;
	m_bEnableStartAnimation	=	1;

/*
	m_iActiveFrame = 0;
	m_iBackFrame = 1;
*/
	m_fFPWeapon=0;
	m_vDifSmooth=Vec3(ZERO);
	m_vDifSmoothRate=Vec3(ZERO);
	m_fAnimSpeedScale = 1;
	m_LastUpdateFrameID_Pre=0;
	m_LastUpdateFrameID_PreType=~0;
	m_LastUpdateFrameID_Post=0;
	m_LastUpdateFrameID_PostType=~0;

	QuatTS identityQuatTS;	identityQuatTS.SetIdentity();

	{
		MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Skinning Transformations");

		// TODO: Pool
		for (int a=0; a<sSkiningTransCnt; ++a)
			m_skinningTransformations[a].resize(numJoints, identityQuatTS);
		m_skinningTransformationsMovement = 0;
	}

	m_arrShapeDeformValues[0]=0.0f;
	m_arrShapeDeformValues[1]=0.0f;
	m_arrShapeDeformValues[2]=0.0f;
	m_arrShapeDeformValues[3]=0.0f;
	m_arrShapeDeformValues[4]=0.0f;
	m_arrShapeDeformValues[5]=0.0f;
	m_arrShapeDeformValues[6]=0.0f;
	m_arrShapeDeformValues[7]=0.0f;

	m_rpFlags = CS_FLAG_DRAW_MODEL;
  m_bUpdateMotionBlurSkinnning = false;
  m_bPrevFrameFPWeapon = false;

  m_trajectoryWeight = 0.0f;
}

//////////////////////////////////////////////////////////////////////////
void CCharInstance::ShutDown()
{
	CSkinInstance::ShutDown();
	m_SkeletonPose.DestroyPhysics();
	KillAllSkeletonEffects();
}

void CCharInstance::DeleteThis()
{
	delete this;
}

//-------------------------------------------------------------------------
void CCharInstance::ReplaceModel(ICharacterModel* pNewModel)
{
	// call super class replace model
	CSkinInstance::ReplaceModel(pNewModel);

  SAFE_RELEASE(m_pFacialInstance);
  m_SkeletonPose.DestroyPhysics();

  Init();

  m_AttachmentManager.ProjectAllAttachment();
}

//////////////////////////////////////////////////////////////////////////
CCharInstance::~CCharInstance()
{
	WaitSkinningTransformationsComputation();

	m_SkeletonPose.DestroyPhysics();
	g_nCharInstanceLoaded--;
	g_nCharInstanceDeleted++;

	// unregister from animation queue
	g_pCharacterManager->RemoveAnimationToSyncQueue( this );
	
	const char* pFilePath = GetFilePath();
	g_pCharacterManager->ReleaseCDF(pFilePath);
}

// calculates the mask ANDed with the frame id that's used to determine whether to skin the character on this frame or not.
uint32 CCharInstance::GetUpdateFrequencyMask(const Vec3& WPos, const CCamera& rCamera)
{
	f32 fRadius  = (m_SkeletonPose.m_AABB.max-m_SkeletonPose.m_AABB.min).GetLength();
	Vec3 WCenter = (m_SkeletonPose.m_AABB.max+m_SkeletonPose.m_AABB.min)*0.5f+WPos;
	f32 fScale	= 2.0f*fRadius; 

	f32 fFOV = rCamera.GetFov();
	f32 fZoomFactor = 0.5f + 0.5f*(RAD2DEG(fFOV)/60.f);
	fZoomFactor *= fZoomFactor*fZoomFactor;
	fZoomFactor *= 4;

	float fScaledDist = WCenter.GetDistance(rCamera.GetPosition())*fZoomFactor;

	uint32 iMask = 7;  //update every 8th frame
	if (m_SkeletonPose.m_bInstanceVisible==0)
		return iMask;

	if(fScaledDist<(90.0f*fScale)) iMask &= 3; //update every 4th frame
	if(fScaledDist<(60.0f*fScale)) iMask &= 1; //update every 2nd frame
	if(fScaledDist<(30.0f*fScale)) iMask &= 0; //update every frame
	return iMask;
}

//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------


void CCharInstance::SkeletonPreProcess(const QuatT &rPhysLocationCurr, const QuatTS &rAnimLocationCurr, const CCamera& rCamera, uint32 OnRender )
{
	SAnimationProcessParams params;
	params.locationPhysics = rPhysLocationCurr;
	params.locationAnimation = rAnimLocationCurr;
	params.bOnRender = OnRender != 0;
	StartAnimationProcessing(params);
}

QuatTS* CCharInstance::GetSkinningTransformations(uint32 frameId)
{
	uint32 index = frameId;
	index %= sSkiningTransCnt;
	return &m_skinningTransformations[index][0];
}

QuatTS* CCharInstance::GetSkinningPrevTransformations(uint32 frameId)
{
	uint32 index = frameId;
	index -= 1;
	index %= sSkiningTransCnt;
	return &m_skinningTransformations[index][0];
}

void CCharInstance::StartAnimationProcessing(const SAnimationProcessParams& params)
{
	DEFINE_PROFILER_FUNCTION();
	ANIMATION_LIGHT_PROFILER();
	m_trajectoryWeight = params.trajectoryWeight;
	m_SkeletonAnim.m_fPostProcessZoomAdjustedDistanceFromCamera = params.zoomAdjustedDistanceFromCamera;

	//float fColor[4] = {1,1,0,1};
	//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"Scaling: %f",rAnimLocationCurr.s );	g_YLine+=16.0f;

	//_controlfp(0, _EM_INVALID|_EM_ZERODIVIDE | _PC_64 );
	m_RenderCharLocation=params.locationAnimation;  //just for debugging

	QuatTS AnimCharLocation=QuatTS(IDENTITY);

	/*uint32 numAttachments = m_AttachmentManager.m_arrAttachments.size();
	for (uint32 i=0; i<numAttachments; i++)
	{
		CAttachment* pAttachment=m_AttachmentManager.m_arrAttachments[i];
		uint32 type=pAttachment->m_Type;
		IAttachmentObject* pIAttachmentObject = pAttachment->GetIAttachmentObject();	
		if (pIAttachmentObject) 
		{
			CSkinInstance* pSkinInstance = (CSkinInstance*)pIAttachmentObject->GetICharacterInstance();
			if (pSkinInstance) 
				pSkinInstance->m_pSkinAttachment=0; 
		}
	}*/

	m_PhysEntityLocation = params.locationPhysics;
	AnimCharLocation = params.locationAnimation;

	pe_params_flags pf;
	IPhysicalEntity *pCharPhys = m_SkeletonPose.GetCharacterPhysics();
	if (Console::GetInst().ca_RandomScaling &&
		m_SkeletonAnim.GetCharEditMode()==0 && 
		m_pModel->m_ObjectType==CHR && 
		!(pCharPhys && pCharPhys->GetType()==PE_ARTICULATED && pCharPhys->GetParams(&pf) && pf.flags & aef_recorded_physics))
	{
		uint32 inst=(uint32(this)>>3)&0x0f;
		AnimCharLocation.s = f32(inst)/160.0f+0.95f;
	}






	uint32 nErrorCode=0;
	uint32 minValid = m_SkeletonPose.m_AABB.min.IsValid();
	if (minValid==0) nErrorCode|=0x8000;
	uint32 maxValid = m_SkeletonPose.m_AABB.max.IsValid();
	if (maxValid==0) nErrorCode|=0x8000;
	if (Console::GetInst().ca_SaveAABB && nErrorCode)
	{
		m_SkeletonPose.m_AABB.max=Vec3( 2, 2, 2);
		m_SkeletonPose.m_AABB.min=Vec3(-2,-2,-2);
	}

	//--- Reset our current time accumulation if it's been consumed
	if (m_SkeletonAnim.m_bTimeUpdated)
	{
		m_fAccDeltaTime = 0.0f;
	}

	float fNewDeltaTime = clamp(g_pITimer->GetFrameTime(), -0.5f, 0.5f);
	m_fOriginalDeltaTime = fNewDeltaTime;
	m_fDeltaTime				 =	fNewDeltaTime*m_fAnimSpeedScale;
	m_fAccDeltaTime			+=	fNewDeltaTime*m_fAnimSpeedScale;

	//---------------------------------------------------------------------------------

	m_SkeletonPose.m_bFullSkeletonUpdate=0;
	int nCurrentFrameID = g_pCharacterManager->m_nUpdateCounter; //g_pIRenderer->GetFrameID(false);
	uint32 dif = nCurrentFrameID-m_LastRenderedFrameID;
	m_SkeletonPose.m_bInstanceVisible = (dif<5) || m_SkeletonAnim.GetTrackViewStatus();


	if (m_SkeletonPose.m_bInstanceVisible)
		m_SkeletonPose.m_bFullSkeletonUpdate=1;
	if (m_SkeletonAnim.m_TrackViewExclusive)
		m_SkeletonPose.m_bFullSkeletonUpdate=1;
	if (m_SkeletonPose.m_nForceSkeletonUpdate)
		m_SkeletonPose.m_bFullSkeletonUpdate=1;
	if (Console::GetInst().ca_ForceUpdateSkeletons != 0)
		m_SkeletonPose.m_bFullSkeletonUpdate=1;

	// depending on how far the character from the player is, the bones may be updated once per several frames
	m_nInstanceUpdateCounter++;
	int nUFM = 0;
	if (params.pCamera)
		nUFM = GetUpdateFrequencyMask(AnimCharLocation.t, *params.pCamera);
	if (m_SkeletonPose.m_bFullSkeletonUpdate)
		nUFM=0;

//	nUFM=7;  //always update every 8th frame

	uint32 FrameMask		= m_nInstanceUpdateCounter&nUFM;
	uint32 InstanceMask = m_nInstanceNumber&nUFM;
	uint32 nQueueUpdate = m_SkeletonPose.m_timeStandingUp<0 ? FrameMask==InstanceMask : true;
	nQueueUpdate|= m_nForceUpdate;

	m_SkeletonAnim.m_IsAnimPlaying=0;
	m_SkeletonAnim.ProcessAnimations( m_PhysEntityLocation, AnimCharLocation, params.bOnRender, nQueueUpdate);
}

void CCharInstance::SkeletonPostProcess(const QuatT &rPhysLocationNext, const QuatTS &rAnimLocationNext, IAttachment* pIAttachment, float fZoomAdjustedDistanceFromCamera, uint32 OnRender) 
{
	QuatTS locationAnimation = rAnimLocationNext;

	m_SkeletonAnim.m_fPostProcessZoomAdjustedDistanceFromCamera = fZoomAdjustedDistanceFromCamera;

	if (m_trajectoryWeight > 0.0f)
	{
		locationAnimation = locationAnimation * QuatT::CreateNLerp(QuatT(IDENTITY), m_SkeletonAnim.GetRelMovement(), m_trajectoryWeight);
	}

	if (pIAttachment)
	{
		assert(pIAttachment->GetType() == CA_BONE || pIAttachment->GetType() == CA_FACE);
		CCharInstance* pInstanceMaster = ((CAttachment*)pIAttachment)->m_pAttachmentManager->m_pSkelInstance;
		m_SkeletonPose.m_bFullSkeletonUpdate = pInstanceMaster->m_SkeletonPose.m_bFullSkeletonUpdate;
		m_fDeltaTime		= pInstanceMaster->m_fDeltaTime;
		m_fAccDeltaTime = pInstanceMaster->m_fAccDeltaTime;
		m_SkeletonAnim.m_IsAnimPlaying = false;
		m_SkeletonAnim.ProcessAnimations(rPhysLocationNext, locationAnimation, OnRender,1, true);
	}

	m_SkeletonPose.SkeletonPostProcess(m_SkeletonPose.GetPoseDataExplicitWriteable(), rPhysLocationNext, locationAnimation, pIAttachment, fZoomAdjustedDistanceFromCamera, OnRender);
	ISkeletonAnim* pSkeletonAnim = GetISkeletonAnim();
	ISkeletonPose* pSkeletonPose = GetISkeletonPose();
	if (pSkeletonAnim && pSkeletonPose)
		m_skeletonEffectManager.Update(pSkeletonAnim, pSkeletonPose, Matrix34(rPhysLocationNext));

	// this is probably unnecessary because it's being resetted back to 0 on queue updates in CCharInstance::StartAnimationProcessing
	m_fAccDeltaTime = 0.0f;
};

//////////////////////////////////////////////////////////////////////////
void CCharInstance::CopyPoseFrom(const ICharacterInstance &instance)
{
	const ISkeletonPose *srcIPose = const_cast<ICharacterInstance &>(instance).GetISkeletonPose();
	CRY_ASSERT_TRACE(srcIPose, ("CopyPoseFrom: %s Copying from an invalid pose!", GetFilePath()));
	if (srcIPose)
	{
		CRY_ASSERT_TRACE(m_SkeletonPose.GetJointCount() == srcIPose->GetJointCount(), ("CopyPoseFrom: %s Joint count mismatch %d vs %d", GetFilePath(), srcIPose->GetJointCount(), m_SkeletonPose.GetJointCount()));
		CRY_ASSERT_TRACE(!m_SkeletonAnim.m_bDidRun, ("CopyPoseFrom: %s Copying into a char whilst it calcs animation", GetFilePath(), srcIPose->GetJointCount(), m_SkeletonPose.GetJointCount()));

		const CSkeletonPose *srcPose = (CSkeletonPose *)srcIPose;
		m_SkeletonPose.GetPoseDataForceWriteable().Initialize(srcPose->GetPoseData());
	}
}

//////////////////////////////////////////////////////////////////////////
void CCharInstance::UpdatePhysicsCGA(Skeleton::CPoseData& poseData, float fScale, const QuatTS& rAnimLocationNext)
{
	DEFINE_PROFILER_FUNCTION();
	CAnimationSet* pAnimationSet = &m_pModel->m_AnimationSet;

	if (!m_SkeletonPose.GetCharacterPhysics())
		return;

	if (m_fOriginalDeltaTime <= 0.0f)
		return;

	QuatT* 		const	__restrict	pAbsolutePose = SPU_PTR_SELECT( &poseData.m_pJointsAbsolute[0], gSkeletonAbsolutePose );

	pe_params_part params;
	pe_action_set_velocity asv;
	pe_status_pos sp;
	int numNodes = poseData.GetJointCount();
	assert(numNodes);

	int i,iLayer,iLast=-1;
	bool bSetVel=0;
	uint32 nType= m_SkeletonPose.GetCharacterPhysics()->GetType();
	bool bBakeRotation = m_SkeletonPose.GetCharacterPhysics()->GetType()==PE_ARTICULATED;
	if (bSetVel = bBakeRotation) 
	{
		for(iLayer=0;iLayer<numVIRTUALLAYERS && m_SkeletonAnim.GetNumAnimsInFIFO(iLayer)==0;iLayer++);
		bSetVel = iLayer<numVIRTUALLAYERS;
	}
	params.bRecalcBBox = false;


	for (i=0; i<numNodes; i++)
	{
SetAgain:
		if (m_SkeletonPose.m_ppBonePhysics && m_SkeletonPose.m_ppBonePhysics[i])
		{
			sp.ipart = 0; MARK_UNUSED sp.partid;
			m_SkeletonPose.m_ppBonePhysics[i]->GetStatus(&sp);
			pAbsolutePose[i].q = !rAnimLocationNext.q*sp.q;
			pAbsolutePose[i].t = !rAnimLocationNext.q*(sp.pos-rAnimLocationNext.t);
			continue;
		}
		if (m_SkeletonPose.m_parrModelJoints[i].m_NodeID==~0)
			continue;
		//	params.partid = joint->m_nodeid;
		params.partid = m_SkeletonPose.m_parrModelJoints[i].m_NodeID;

		assert( pAbsolutePose[i].IsValid() );
		params.q		=	pAbsolutePose[i].q;

		params.pos	= pAbsolutePose[i].t*rAnimLocationNext.s;
		if (rAnimLocationNext.s != 1.0f)
			params.scale = rAnimLocationNext.s;

		if (bBakeRotation)
		{
			params.pos = rAnimLocationNext.q*params.pos;
			params.q = rAnimLocationNext.q*params.q;
		}
		if (m_SkeletonPose.GetCharacterPhysics()->SetParams( &params ))
			iLast = i;
		if (params.bRecalcBBox)
			break;
	}

	// Recompute box after.
	if (iLast>=0 && !params.bRecalcBBox) 
	{
		params.bRecalcBBox = true;
		i = iLast;
		goto SetAgain;
	}

	if (bSetVel)
	{
		ApplyJointVelocitiesToPhysics(m_SkeletonPose.GetCharacterPhysics(), rAnimLocationNext.q);
		m_bPlayedPhysAnim = 1;
	} else if (bBakeRotation && m_bPlayedPhysAnim)
	{
		asv.v.zero(); asv.w.zero();
		for(i=0; i<numNodes; i++) if ((asv.partid = m_SkeletonPose.m_parrModelJoints[i].m_NodeID)!=~0)
			m_SkeletonPose.GetCharacterPhysics()->Action(&asv);			
		m_bPlayedPhysAnim = 0;
	}

	if (m_SkeletonPose.m_ppBonePhysics)
		m_SkeletonPose.UpdateBBox(1);
}


void CCharInstance::ApplyJointVelocitiesToPhysics(IPhysicalEntity *pent, const Quat &qrot, const Vec3 &velHost)
{
	int i,iParent,numNodes=m_SkeletonPose.GetPoseData().GetJointCount();
	QuatT qParent;
	IController* pController;
	float t,dt;
	pe_action_set_velocity asv;
	CAnimationSet* pAnimationSet = &m_pModel->m_AnimationSet;

	QuatT qAnimCur[MAX_JOINT_AMOUNT],qAnimNext[MAX_JOINT_AMOUNT];
	for(i=0; i<numNodes; i++)
		qAnimCur[i]=qAnimNext[i] = m_SkeletonPose.GetPoseData().m_pJointsRelative[i];

	if (!m_SkeletonAnim.m_layers[0].m_transitionQueue.m_animations.size())
		return;
	CAnimation &anim = m_SkeletonAnim.GetAnimFromFIFO(0,0);

	int idGlobal = pAnimationSet->GetGlobalIDByAnimID_Fast(anim.m_nAnimID);
	GlobalAnimationHeaderCAF& animHdr = g_AnimationManager.m_arrGlobalCAF[idGlobal];

	const float FRAME_TIME = 0.01f;
	const float fAnimDuration = max(animHdr.m_fEndSec - animHdr.m_fStartSec, 1e-5f);
	dt = static_cast<float>(__fsel(fAnimDuration - FRAME_TIME, FRAME_TIME / fAnimDuration, 1.0f));
	t = min(1.0f-dt, anim.m_fAnimTime);

	for(i=0; i<numNodes; i++)	if (pController = animHdr.GetControllerByJointCRC32(m_pModel->m_ModelSkeleton.m_arrModelJoints[i].m_nJointCRC32))//m_pModel->m_ModelSkeleton.m_arrModelJoints[i].m_arrControllersMJoint[idAnim])
		pController->GetOP( animHdr.NTime2KTime(t), qAnimCur[i].q, qAnimCur[i].t),
		pController->GetOP( animHdr.NTime2KTime(t+dt), qAnimNext[i].q, qAnimNext[i].t);

	for(i=0; i<numNodes; i++) if ((iParent=m_SkeletonPose.m_parrModelJoints[i].m_idxParent)>=0 && iParent!=i)
		qAnimCur[i] =	qAnimCur[iParent]*qAnimCur[i], qAnimNext[i] =	qAnimNext[iParent]*qAnimNext[i];

	const float INV_FRAME_TIME = 1.0f / FRAME_TIME;
	for(i=0; i<numNodes; i++) if (m_SkeletonPose.m_parrModelJoints[i].m_NodeID!=~0)
	{
		asv.partid = m_SkeletonPose.m_parrModelJoints[i].m_NodeID;
		asv.v = qrot*((qAnimNext[i].t-qAnimCur[i].t)*INV_FRAME_TIME) + velHost;
		Quat dq = qAnimNext[i].q*!qAnimCur[i].q;
		float sin2 = dq.v.len();
		asv.w = qrot*(sin2>0 ? dq.v*(atan2_tpl(sin2*dq.w*2,dq.w*dq.w-sin2*sin2)*INV_FRAME_TIME/sin2) : Vec3(0));
		pent->Action(&asv);
	}
}


//----------------------------------------------------------------------
//----------------------------------------------------------------------
//----------------------------------------------------------------------

AABB CCharInstance::GetAABB() 
{ 
	return m_SkeletonPose.GetAABB();	
}

float CCharInstance::GetRadiusSqr()
{
	return m_SkeletonPose.GetAABB().GetRadiusSqr();	
}

float CCharInstance::GetExtent(EGeomForm eForm)
{
	if (!m_SkeletonPose.m_arrCGAJoints.empty())
	{
		CGeomExtent& extent = m_SkeletonPose.m_Extents.Make(eForm);
		if (!extent)
		{
			extent.ReserveParts(1 + m_SkeletonPose.m_arrCGAJoints.size());

			// Base class handles ModelMesh, and char attachments.
			extent.AddPart(CSkinInstance::GetExtent(eForm));

			// Handle CGA joints here.
			for_container (Array<CCGAJoint>, pJoint, m_SkeletonPose.m_arrCGAJoints)
			{
				if (pJoint->m_CGAObjectInstance)
					extent.AddPart( pJoint->m_CGAObjectInstance->GetExtent(eForm) );
				else
					extent.AddPart(0.f);
			}
			extent.TrimParts();
		}
		return extent.TotalExtent();
	}
	return CSkinInstance::GetExtent(eForm);
}

void CCharInstance::GetRandomPos(PosNorm& ran, EGeomForm eForm)
{
	if (!m_SkeletonPose.m_arrCGAJoints.empty())
	{
		CGeomExtent const& ext = m_SkeletonPose.m_Extents[eForm];
		int iPart = ext.RandomPart();
		if (iPart-- > 0)
		{
			// Use CGA joint.
			assert(m_pModel && m_pModel->m_ObjectType == CGA);
			CCGAJoint const* pJoint = &m_SkeletonPose.m_arrCGAJoints[iPart];
			pJoint->m_CGAObjectInstance->GetRandomPos(ran, eForm);
			Transform(ran, m_SkeletonPose.GetPoseData().m_pJointsAbsolute[iPart]);
			return;
		}
	}

	CSkinInstance::GetRandomPos(ran, eForm);
}

//-----------------------------------------------------------------------------
//-----  Spawn decal on the character                                     -----
//-----------------------------------------------------------------------------
void CCharInstance::CreateDecal(CryEngineDecalInfo& DecalLCS)
{
	if (m_pModel->m_ObjectType==CGA)
		return;
	if (Console::GetInst().ca_UseDecals==0)
		return;
	if ( fabsf(DecalLCS.fSize) < 0.001f)
		return;

	//-----------------------------------------------------------------------------------

  int nList = (int)CharacterManager::GetRendererMainThreadId();

  QuatTS* pSkinningTransformations = GetSkinningTransformations(nList);
	m_DecalManager.CreateDecalsOnInstance(pSkinningTransformations, m_pModel, &m_Morphing, m_arrShapeDeformValues, DecalLCS, m_PhysEntityLocation);

	uint32 numAttachments = m_AttachmentManager.m_arrAttachments.size();
	for (uint32 a=0; a<numAttachments; a++)
	{
		CAttachment* pAttachment=m_AttachmentManager.m_arrAttachments[a];
		uint32 type=pAttachment->m_Type;
		if (type != CA_SKIN && type != CA_PART) 
			continue;

		IAttachmentObject* pIAttachmentObject = pAttachment->GetIAttachmentObject();	
		if (pIAttachmentObject) 
		{
			CSkinInstance* pSkinInstance = (CSkinInstance*)pIAttachmentObject->GetICharacterInstance();
			if (pSkinInstance) 
			{
				QuatTS arrNewSkinQuat[MAX_JOINT_AMOUNT+1];	//bone quaternions for skinning (entire skeleton)
//				uint32 iActiveFrame		=	m_iActiveFrame;
				uint32 numRemapJoints	= pAttachment->m_arrRemapTable.size();
				for (uint32 i=0; i<numRemapJoints; i++)	
					arrNewSkinQuat[i] = pSkinningTransformations[pAttachment->m_arrRemapTable[i]];
				pSkinInstance->m_DecalManager.CreateDecalsOnInstance(arrNewSkinQuat,pSkinInstance->m_pModel,&pSkinInstance->m_Morphing,m_arrShapeDeformValues, DecalLCS, m_PhysEntityLocation);
			}
		}
	}
}


void CCharInstance::OnDetach()
{
	m_pSkinAttachment=0;
	pe_params_rope pr;
	pr.pEntTiedTo[0]=pr.pEntTiedTo[1] = 0;
	m_SkeletonPose.SetAuxParams(&pr);
}

// Skinning

void CCharInstance::BeginSkinningTransformationsComputation()
{
	int nList = (int)CharacterManager::GetRendererMainThreadId();

	// Make sure we finished the previous started task.
	m_skinningTransformationsComputationTask.Wait();

	m_skinningTransformationsComputationTask.Begin(
		m_pModel->m_ModelSkeleton, m_SkeletonPose.GetPoseData(),
		(QuatD*)GetSkinningTransformations(nList),
		(QuatD*)GetSkinningPrevTransformations(nList),
		GetSkinningTransformationCount(),
		&m_skinningTransformationsMovement);
}

void CCharInstance::WaitSkinningTransformationsComputation(QuatTS** pCurrent, QuatTS** pLast)
{
	m_skinningTransformationsComputationTask.Wait();
	uint32 frameId = CharacterManager::GetRendererThreadId();

	if (pCurrent)
		*pCurrent = (QuatTS*)GetSkinningTransformations(frameId);
	if (pLast)
		*pLast = (QuatTS*)GetSkinningPrevTransformations(frameId);
}

//////////////////////////////////////////////////////////////////////////
void CCharInstance::Serialize(TSerialize ser)
{
	if (ser.GetSerializationTarget() != eST_Network)
	{
		MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Character instance serialization");

		ser.BeginGroup("CSkinInstance");
		ser.Value("fAnimSpeedScale", (float&)(m_fAnimSpeedScale));
		ser.EndGroup();

		m_SkeletonAnim.Serialize(ser);
		m_AttachmentManager.Serialize(ser); 

		if (ser.IsReading())
		{
			// Make sure that serialized characters that are ragdoll are updated even if their physic entity is sleeping
			m_SkeletonPose.m_bPhysicsWasAwake = true;
		}
	}
}



//////////////////////////////////////////////////////////////////////////
size_t CCharInstance::SizeOfCharInstance()
{

	size_t nSizeOfCharInstance	= 0;
	//--------------------------------------------------------------------
	//---        this is the size of the SkinInstance class           ----
	//--------------------------------------------------------------------
	{
		size_t size = sizeof(CAttachmentManager) + m_AttachmentManager.SizeOfThis();
		nSizeOfCharInstance += size;
	}

	{
		size_t size		= sizeof(CAnimDecalManager) + m_DecalManager.SizeOfThis();
		nSizeOfCharInstance += size;
	}

	{
		size_t size	= sizeof(CMorphing);
		size +=	m_Morphing.SizeOfThis();
		nSizeOfCharInstance += size;
	}

	{
		size_t size		= sizeof(CFacialInstance);
		if (m_pFacialInstance)
			size = m_pFacialInstance->SizeOfThis();
		nSizeOfCharInstance += size;
	}

	{
		size_t size		= sizeof(CSkeletonEffectManager);
		size += m_skeletonEffectManager.SizeOfThis();
		nSizeOfCharInstance += size;
	}

	//--------------------------------------------------------------------
	//---        this is the size of the CharInstance class           ----
	//--------------------------------------------------------------------

	{
		size_t size	= sizeof(CSkeletonAnim)+m_SkeletonAnim.SizeOfThis();
		nSizeOfCharInstance += size;
	}

	{
		size_t size	= sizeof(CSkeletonPose)+m_SkeletonPose.SizeOfThis();
		nSizeOfCharInstance += size;
	}

	{
	//	size_t size	= sizeof(CAnimActionActor)+m_AnimActionActor.SizeOfThis(pSizer);
	//	pSizer->AddObject(&m_AnimActionActor, size);
	//	nSizeOfCharInstance += size;
	}

	{
		size_t _size = 0;
		for (size_t i=0; i<sSkiningTransCnt; ++i)
			_size += stl::size_of_aligned_vector( m_skinningTransformations[i] );
		nSizeOfCharInstance += _size;
	}

	//--------------------------------------------------------------------
	//---          evaluate the real size of CCharInstance            ----
	//--------------------------------------------------------------------

	{
		size_t size		=	sizeof(CCharInstance) - sizeof(CAttachmentManager)-sizeof(CAnimDecalManager)-sizeof(CMorphing)-sizeof(CSkeletonAnim)-sizeof(CSkeletonPose)-sizeof(CSkeletonEffectManager);
		size += m_strFilePath.capacity();

		size += m_IgnoreScalingJoints.get_alloc_size();
		size += m_IgnoreScale.get_alloc_size(); 
		size += m_bCustomScale.get_alloc_size(); 
		size += m_fCustomScale.get_alloc_size(); 
		nSizeOfCharInstance += size;
	}

	return nSizeOfCharInstance;
};

void CCharInstance::GetMemoryUsage(ICrySizer * pSizer) const
{
	pSizer->AddObject(this, sizeof(*this));

	//--------------------------------------------------------------------
	//---        this is the size of the SkinInstance class           ----
	//--------------------------------------------------------------------
	{
		SIZER_COMPONENT_NAME(pSizer, "CAttachmentManager");
		pSizer->AddObject( m_AttachmentManager );		
	}

	{
		SIZER_COMPONENT_NAME(pSizer, "DecalManager");
		pSizer->AddObject( m_DecalManager );
	}

	{
		SIZER_COMPONENT_NAME(pSizer, "Morphing");		
		pSizer->AddObject( m_Morphing );
	}

	{
		SIZER_COMPONENT_NAME(pSizer, "FacialInstance");
		pSizer->AddObject( m_pFacialInstance );		
	}

	{
		SIZER_COMPONENT_NAME(pSizer, "skeletonEffectManager");		
		pSizer->AddObject( m_skeletonEffectManager );
	}

	//--------------------------------------------------------------------
	//---        this is the size of the CharInstance class           ----
	//--------------------------------------------------------------------

	{
		SIZER_COMPONENT_NAME(pSizer, "SkeletonAnim");
		pSizer->AddObject( m_SkeletonAnim );
	}

	{
		SIZER_COMPONENT_NAME(pSizer, "SkeletonPose");
		pSizer->AddObject( m_SkeletonPose );
	}

	{
		//SIZER_COMPONENT_NAME(pSizer, "AnimActionActor");
		//pSizer->AddObject( m_AnimActionActor );
	}

	{
		SIZER_COMPONENT_NAME(pSizer, "NewSkinQuat");
		for(size_t i=0; i<sSkiningTransCnt; ++i)
		{
			if (!m_skinningTransformations[i].empty())
			{
				pSizer->AddObject( &(m_skinningTransformations[i].front()),stl::size_of_aligned_vector(m_skinningTransformations[i]) );
			}
		}		
	}

	//--------------------------------------------------------------------
	//---          evaluate the real size of CCharInstance            ----
	//--------------------------------------------------------------------

	{
		SIZER_COMPONENT_NAME(pSizer, "CCharInstance");
		pSizer->AddObject( m_strFilePath );

		pSizer->AddObject( m_IgnoreScalingJoints );
		pSizer->AddObject( m_IgnoreScale );
		pSizer->AddObject( m_bCustomScale );
		pSizer->AddObject( m_fCustomScale );
	} 

	CSkinInstance::GetMemoryUsage(pSizer);
};
